#![allow(overflowing_literals)]

fn casting(){
    let decimal = 65.4321_f32;

    let integer: u8 = decimal as u8; // OK (f32->u8)

    let character = integer as char; // OK (i32->char)

    // let character = decimal as char; // Error: f32->char

    println!("Casting: {} -> {} -> {}", decimal, integer, character);

    println!("1000 as u16 is : {}", 1000 as u16);
    println!("1000 as u8 is : {}", 1000 as u8);
    println!("1000 mod 256 : {}", 1000 % 256);

    println!("1000 as a u8 is : {}", 1000 as u8);
    println!(" 232 as a i8 is : {}", 232 as i8);

    println!("128 as a i16 is : {}", 128 as i16);
    println!("128 as a i8 is : {}", 128 as i8);


    // saturating cast 
    
    println!("300.0 is {}", 300.0_f32 as u8); // 300.00 to u8 => upper boundary([255]~0)
    println!("-100.0 as u8 is {}", -100.0_f32 as u8); //-100 to u8 => lower boundary (255~[0])

    println!("nan as u8 is {}", f32::NAN as u8); // NAN is 0

    unsafe {
        //300.0 is 44 
        println!("300.0 is {}", 300.0_f32.to_int_unchecked::<u8>());

        //-100.0 as u8 is 156
        println!("-100.0 as u8 is {}", (-100.0_f32).to_int_unchecked::<u8>());

        //nan as u8 is 0
        println!("nan as u8 is {}", f32::NAN.to_int_unchecked::<u8>());
    }
}

fn literals(){
    let x = 1u8;
    println!("size of x in bytes: {}", std::mem::size_of_val(&x));
    let y = 2u32;
    println!("size of y in bytes: {}", std::mem::size_of_val(&y));
    let z = 3f32;
    println!("size of z in bytes: {}", std::mem::size_of_val(&z));

    let i = 1;
    println!("size of i in bytes: {}", std::mem::size_of_val(&i));
    let f = 1.0;
    println!("size of f in bytes: {}", std::mem::size_of_val(&f));
}

fn inference(){
    let mut vec = Vec::new();

    let elem = 5u8;
    vec.push(elem);

    println!("inference {:?}", vec);
}

fn aliasing(){
    type NanoSecond = u64;
    type Inch = u64;
    type U64 = u64;

    let nano: NanoSecond = 5 as U64;
    let inches: Inch = 2 as U64;

    println!("{} nanoseconds + {} inches = {} unit?", nano, inches, nano + inches);
}

use std::fmt::{Debug};
fn type_v<T>(t: T) where T: Debug {
    println!("type name of {:?} -> {}", t, std::any::type_name::<T>());
}

fn array(){
    //[T; N] => array 
    //&[T; N] => ref of array
    //[T] => Slice : dynamically-sized, see: std::slice document
    //&[T] => ref of slice 
    {
        let array:[u32; 10] = [0_u32; 10];    //[T; N] => array ;
        type_v(array);
    }

    {
        let array:[u32; 10] = [0_u32; 10];
        let array_ref = &array;      //&[T; N] => ref of array
        type_v(array_ref);
    }
    {
        let _array:[u32; 10] = [0_u32; 10];
        //let slice = array[0..1];    //[T] => Slice
        //complie error: doesn't have a size known at compile-time
    }
    {
        let array:[u32; 10] = [0_u32; 10];
        let slice = &array[0..1];    //&[T] => ref of Slice
        type_v(slice);
    }


    {
        let array:[u32; 10]  =[0_u32; 10];
        let array2: [u32; 10] = [0_u32; 10];

        //1. array == array
        if array == array2 {
            assert_eq!(1, 1);
        }
        else{
            assert_eq!(1, 0);
        }

        //2. array == &array => unsupported
        /*
        if array == &array2 {
            assert_eq!(1, 1);
        }
        else{
            assert_eq!(1, 0);
        }*/

        //3. array == [T] slice 
        if array == array[..] {
            assert_eq!(1, 1);
        }
        else{
            assert_eq!(1, 0);
        }

        //4. array == &slice; 
        let slice_ref = &array[..];
        if slice_ref == array {
            assert_eq!(1, 1);
        }
        else{
            assert_eq!(1, 0);
        }
    }
}

pub fn main(){
    casting();
    literals();
    inference();
    aliasing();

    array();
}
